package fiuba.algo3.algoThieft.modelo;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;



public class Juego extends ObjetoObservable implements Deserializable, Persistible {
	private static final int DURACION_EMISION_ORDEN_DE_ARRESTO = 3;

	private static final int TIEMPO_LIMITE = 161;
	
    private Tiempo reloj;
    private Personaje personaje;
    private HashMap<String,Personaje> personajes;
    private Interpol interpol;
    
    public Juego() throws ParserConfigurationException, SAXException, IOException {
        this.interpol = new Interpol();
        this.personajes = new HashMap<String,Personaje>();
        Persistor persistor = new Persistor();
        persistor.deserializarDesdeElArchivo(this , "personajes.xml" );
    }


    public int getTiempoEnHoras () throws ErrorNoSeInicioUnNuevoCaso {
    	this.verificarSiHayUnCaso();
        return this.reloj.getTiempoEnHoras();
    }

    public Pista gestionarVisita(Lugar lugar) throws ErrorNoSeInicioUnNuevoCaso {
    	this.verificarSiHayUnCaso();
    	if ( lugar.hayAlguienEscondido() ){
    		lugar.interactuarConResidente(this);
    	}
        reloj.avanzar ( lugar.duracionDeVisita() );
        verificarSiSeTerminoElTiempo ();
        return personaje.recibirPista(lugar); 
    }
    
    public void gestionarViaje (Capital capital) throws ErrorNoSeInicioUnNuevoCaso{
    	this.verificarSiHayUnCaso();
        Viaje viaje = new Viaje(this.personaje.getUbicacion(),capital);
        int velocidadViaje = this.personaje.getVelocidadEnKilometrosPorHora();
        this.reloj.avanzar( viaje.obtenerDemora( velocidadViaje ) );
        personaje.viajar(capital);
        verificarSiSeTerminoElTiempo ();
    }
    
    public void utilizarPersonaje(PersonajePolicial personaje) throws ErrorNoExisteElPersonaje{
    	if (!(this.existePersonaje(personaje) ) ) {
    		throw new  ErrorNoExisteElPersonaje();
		}
    	this.personaje = this.personajes.get( personaje.getNombre() );
    	personaje = (PersonajePolicial)this.personaje;
    }
    
    public void guardarAvances() throws ParserConfigurationException, TransformerException{
        Persistor persistor = new Persistor();
        persistor.guardarEn(this,"personajes.xml");
    }

    @Override
    public void deserializar(Document doc) {
        Element elementoPersonajes = (Element) doc.getElementsByTagName("Personajes").item( 0 );
        for ( int i=0 ; i < elementoPersonajes.getChildNodes().getLength() ; i++ ){
            Personaje personaje = Personaje.crearPersonaje ( elementoPersonajes.getChildNodes().item( i ) );
            this.personajes.put(personaje.getNombre() , personaje);
        }
    }

    @Override
    public Element serializar(Document doc) {
        Element elementoPersonajes = doc.createElement("Personajes");
        Collection<Personaje> coleccionDePersonajes = this.personajes.values();
        for (Personaje personaje : coleccionDePersonajes) {
            elementoPersonajes.appendChild(personaje.serializar(doc));
        }
        return elementoPersonajes;
    }

    public PersonajePolicial getPersonajeActual() {
        return this.personaje;
    }

    public void iniciarCaso() throws SAXException, IOException, ParserConfigurationException, ErrorPersonajeNoAsignado {
        if (personaje == null){
        	throw new ErrorPersonajeNoAsignado();
        }
    	this.reloj = new Tiempo();
        this.interpol.nuevoCaso(personaje);
        this.personaje.setUbicacionInicial(this.interpol.getCaso().getUbicacionInicial());  
            
    }
    
    public boolean existePersonaje(PersonajePolicial pepe){
    	String nombre = pepe.getNombre();
    	return this.personajes.containsKey(nombre);
    }


	public void agregarPersonaje(PersonajePolicial personaje) throws ErrorYaExisteElPersonaje {
		if (this.existePersonaje(personaje) ){
			throw new  ErrorYaExisteElPersonaje();
		}
		this.personajes.put( personaje.getNombre() , (Personaje)personaje);
		
	}


	public void eliminarPersonaje(PersonajePolicial personaje) throws ErrorNoExisteElPersonaje {
		if ( ! (this.existePersonaje(personaje) ) ){
			throw new  ErrorNoExisteElPersonaje();
		}
		this.personajes.remove(personaje.getNombre());
	}
	
	public boolean ordenDeArrestoEmitida(){
		return this.interpol.ordenDeArrestoEmitida();
	}
	
	public void interactuarConResidente(Ladron ladron) {

		if ( this.ordenDeArrestoEmitida() ){
			interpol.intentarArrestar();
		}
		if ( this.interpol.getCaso().elLadronFueArrestado() ){
		    this.personaje.agregarArrestoExitoso();
		}
		this.interpol.casoTerminado();
		this.actualizarObservadores();
		
		
	}
	
	private void verificarSiHayUnCaso() throws ErrorNoSeInicioUnNuevoCaso{
		if (this.interpol.getCaso() == null){
    		throw new  ErrorNoSeInicioUnNuevoCaso();
    	}
	}


	public void interactuarConResidente(Secuaz secuaz) {
		this.reloj.avanzar(secuaz.lanzarProyectil());
		verificarSiSeTerminoElTiempo ();
		
	}


	public boolean estaInvestigandoUncaso() {
		
		return this.interpol.getEstadoDelCaso() == Interpol.ESTADO_VALIDO;
	}


	public List<Ladron> gestionarEmisionDeOrdenDeArresto(String sexo, String hobby,
			String cabello, String senia, String vehiculo) throws ErrorNoSeInicioUnNuevoCaso {
		this.verificarSiHayUnCaso();
		List<Ladron> lista = this.interpol.emitirOrdenDeArresto
							 (sexo, hobby, cabello, senia, vehiculo);
		this.reloj.avanzar(DURACION_EMISION_ORDEN_DE_ARRESTO);
		verificarSiSeTerminoElTiempo ();
		
		return lista;
	}
	
	private void verificarSiSeTerminoElTiempo () {
		if (this.reloj.getTiempoEnHoras() >= TIEMPO_LIMITE ){
			this.interpol.casoTerminadoPorTiempo();
			this.actualizarObservadores();
		}
	}


	public String getSexoDelLadron() throws ErrorNoSeInicioUnNuevoCaso {
		this.verificarSiHayUnCaso();
		return this.interpol.getCaso().getSexoDelLadron();
	}
	
	public int estadoDeLaInvestigacion(){
		return this.interpol.getEstadoDelCaso();
	}


	public ObjetoRobado obtenerObjetoRobado() {
		
		return this.interpol.getCaso().getObjetoRobadoActual();
	}


	public String getTiempoComoString() {
		return this.reloj.comoString();
		
	}
}
